/*
 * @license
 *
 * Multiselect v2.2.6
 * http://crlcu.github.io/multiselect/
 *
 * Copyright (c) 2015 Adrian Crisan
 * Licensed under the MIT license (https://github.com/crlcu/multiselect/blob/master/LICENSE)
 */

if (typeof jQuery === 'undefined') {
	throw new Error('multiselect requires jQuery');
}

;
(function($) {
	'use strict';

	var version = $.fn.jquery.split(' ')[0].split('.');

	if (version[0] < 2 && version[1] < 7) {
		throw new Error('multiselect requires jQuery version 1.7 or higher');
	}
})(jQuery);

;
(function(factory) {
	if (typeof define === 'function' && define.amd) {
		// AMD. Register as an anonymous module depending on jQuery.
		define([ 'jquery' ], factory);
	} else {
		// No AMD. Register plugin with global jQuery object.
		factory(jQuery);
	}
}
		(function($) {
			'use strict';

			var Multiselect = (function($) {
				/**
				 * Multiselect object constructor
				 * 
				 * @class Multiselect
				 * @constructor
				 */
				function Multiselect($select, settings) {
					var id = $select.prop('id');
					this.$left = $select;
					this.$right = $(settings.right).length ? $(settings.right)
							: $('#' + id + '_to');
					this.actions = {
						$leftAll : $(settings.leftAll).length ? $(settings.leftAll)
								: $('#' + id + '_leftAll'),
						$rightAll : $(settings.rightAll).length ? $(settings.rightAll)
								: $('#' + id + '_rightAll'),
						$leftSelected : $(settings.leftSelected).length ? $(settings.leftSelected)
								: $('#' + id + '_leftSelected'),
						$rightSelected : $(settings.rightSelected).length ? $(settings.rightSelected)
								: $('#' + id + '_rightSelected'),

						$undo : $(settings.undo).length ? $(settings.undo)
								: $('#' + id + '_undo'),
						$redo : $(settings.redo).length ? $(settings.redo)
								: $('#' + id + '_redo')
					};

					delete settings.leftAll;
					delete settings.leftSelected;
					delete settings.right;
					delete settings.rightAll;
					delete settings.rightSelected;

					this.options = {
						keepRenderingSort : settings.keepRenderingSort,
						submitAllLeft : settings.submitAllLeft !== undefined ? settings.submitAllLeft
								: true,
						submitAllRight : settings.submitAllRight !== undefined ? settings.submitAllRight
								: true,
						search : settings.search
					};

					delete settings.keepRenderingSort, settings.submitAllLeft,
							settings.submitAllRight, settings.search;

					this.callbacks = settings;

					this.init();
				}

				Multiselect.prototype = {
					// Vars
					undoStack : [],
					redoStack : [],

					// Functions
					init : function() {
						var self = this;

						// For the moment disable sort and search if there is a
						// optgroup element
						if (self.$left.find('optgroup').length
								|| self.$right.find('optgroup').length) {
							self.callbacks.sort = false;
							self.options.search = false;
						}

						if (self.options.keepRenderingSort) {
							self.skipInitSort = true;

							self.callbacks.sort = function(a, b) {
								return $(a).data('position') > $(b).data(
										'position') ? 1 : -1;
							};

							self.$left.find('option').each(
									function(index, option) {
										$(option).data('position', index);
									});

							self.$right.find('option').each(
									function(index, option) {
										$(option).data('position', index);
									});
						}

						if (typeof self.callbacks.startUp == 'function') {
							self.callbacks.startUp(self.$left, self.$right);
						}

						if (!self.skipInitSort
								&& typeof self.callbacks.sort == 'function') {
							self.$left.mSort(self.callbacks.sort);

							self.$right.each(function(i, select) {
								$(select).mSort(self.callbacks.sort);
							});
						}

						// Append left filter
						if (self.options.search && self.options.search.left) {
							self.options.search.$left = $(self.options.search.left);
							self.$left.before(self.options.search.$left);
						}

						// Append right filter
						if (self.options.search && self.options.search.right) {
							self.options.search.$right = $(self.options.search.right);
							self.$right.before($(self.options.search.$right));
						}

						// Initialize events
						self.events();
					},

					events : function() {
						var self = this;

						// Attach event to left filter
						if (self.options.search && self.options.search.$left) {
							self.options.search.$left.on('keyup', function(e) {
								if (this.value) {
									var $toShow = self.$left.find(
											'option:search("' + this.value
													+ '")').mShow();
									var $toHide = self.$left.find(
											'option:not(:search("' + this.value
													+ '"))').mHide();
								} else {
									self.$left.find('option').mShow();
								}
							});
						}

						// Attach event to right filter
						if (self.options.search && self.options.search.$right) {
							self.options.search.$right.on('keyup', function(e) {
								if (this.value) {
									var $toShow = self.$right.find(
											'option:search("' + this.value
													+ '")').mShow();
									var $toHide = self.$right.find(
											'option:not(:search("' + this.value
													+ '"))').mHide();
								} else {
									self.$right.find('option').mShow();
								}
							});
						}

						// Select all the options from left and right side when
						// submiting the parent form
						self.$right.closest('form').on(
								'submit',
								function(e) {
									self.$left.find('option').prop('selected',
											self.options.submitAllLeft);
									self.$right.find('option').prop('selected',
											self.options.submitAllRight);
								});

						// Attach event for double clicking on options from left
						// side
						self.$left.on('dblclick', 'option', function(e) {
							e.preventDefault();

							var $options = self.$left.find('option:selected');

							if ($options.length) {
								self.moveToRight($options, e);
							}
						});

						// Attach event for double clicking on options from
						// right side
						self.$right.on('dblclick', 'option', function(e) {
							e.preventDefault();

							var $options = self.$right.find('option:selected');

							if ($options.length) {
								self.moveToLeft($options, e);
							}
						});

						// dblclick support for IE
						if (navigator.userAgent.match(/MSIE/i)
								|| navigator.userAgent.indexOf('Trident/') > 0
								|| navigator.userAgent.indexOf('Edge/') > 0) {
							self.$left.dblclick(function(e) {
								self.actions.$rightSelected.trigger('click');
							});

							self.$right.dblclick(function(e) {
								self.actions.$leftSelected.trigger('click');
							});
						}

						self.actions.$rightSelected.on('click', function(e) {
							e.preventDefault();

							var $options = self.$left.find('option:selected');

							if ($options.length) {
								self.moveToRight($options, e);
							}

							$(this).blur();
						});

						self.actions.$leftSelected.on('click', function(e) {
							e.preventDefault();

							var $options = self.$right.find('option:selected');

							if ($options.length) {
								self.moveToLeft($options, e);
							}

							$(this).blur();
						});

						self.actions.$rightAll.on('click', function(e) {
							e.preventDefault();

							var $options = self.$left
									.children(':not(span):not(.hidden)');

							if ($options.length) {
								self.moveToRight($options, e);
							}

							$(this).blur();
						});

						self.actions.$leftAll.on('click', function(e) {
							e.preventDefault();

							var $options = self.$right
									.children(':not(span):not(.hidden)');

							if ($options.length) {
								self.moveToLeft($options, e);
							}

							$(this).blur();
						});

						self.actions.$undo.on('click', function(e) {
							e.preventDefault();

							self.undo(e);
						});

						self.actions.$redo.on('click', function(e) {
							e.preventDefault();

							self.redo(e);
						});
					},

					moveToRight : function($options, event, silent, skipStack) {
						var self = this;

						if (typeof self.callbacks.moveToRight == 'function') {
							return self.callbacks.moveToRight(self, $options,
									event, silent, skipStack);
						} else {
							if (typeof self.callbacks.beforeMoveToRight == 'function'
									&& !silent) {
								if (!self.callbacks.beforeMoveToRight(
										self.$left, self.$right, $options)) {
									return false;
								}
							}

							$options.each(function(index, option) {
								var $option = $(option);

								if ($option.parent().is('optgroup')) {
									var $leftGroup = $option.parent();
									var $rightGroup = self.$right
											.find('optgroup[label="'
													+ $leftGroup.prop('label')
													+ '"]');

									if (!$rightGroup.length) {
										$rightGroup = $leftGroup.clone();
										$rightGroup.children().remove();
									}

									$option = $rightGroup.append($option);

									$leftGroup.removeIfEmpty();
								}

								self.$right.move($option);
							});

							if (!skipStack) {
								self.undoStack.push([ 'right', $options ]);
								self.redoStack = [];
							}

							if (typeof self.callbacks.sort == 'function'
									&& !silent) {
								self.$right.mSort(self.callbacks.sort);
							}

							if (typeof self.callbacks.afterMoveToRight == 'function'
									&& !silent) {
								self.callbacks.afterMoveToRight(self.$left,
										self.$right, $options);
							}

							return self;
						}
					},

					moveToLeft : function($options, event, silent, skipStack) {
						var self = this;

						if (typeof self.callbacks.moveToLeft == 'function') {
							return self.callbacks.moveToLeft(self, $options,
									event, silent, skipStack);
						} else {
							if (typeof self.callbacks.beforeMoveToLeft == 'function'
									&& !silent) {
								if (!self.callbacks.beforeMoveToLeft(
										self.$left, self.$right, $options)) {
									return false;
								}
							}

							$options
									.each(function(index, option) {
										var $option = $(option);

										if ($option.is('optgroup')
												|| $option.parent().is(
														'optgroup')) {
											var $rightGroup = $option
													.is('optgroup') ? $option
													: $option.parent();
											var $leftGroup = self.$left
													.find('optgroup[label="'
															+ $rightGroup
																	.prop('label')
															+ '"]');

											if (!$leftGroup.length) {
												$leftGroup = $rightGroup
														.clone();
												$leftGroup.children().remove();
											}

											$option = $leftGroup.append($option
													.is('optgroup') ? $option
													.children() : $option);

											$rightGroup.removeIfEmpty();
										}

										self.$left.move($option);
									});

							if (!skipStack) {
								self.undoStack.push([ 'left', $options ]);
								self.redoStack = [];
							}

							if (typeof self.callbacks.sort == 'function'
									&& !silent) {
								self.$left.mSort(self.callbacks.sort);
							}

							if (typeof self.callbacks.afterMoveToLeft == 'function'
									&& !silent) {
								self.callbacks.afterMoveToLeft(self.$left,
										self.$right, $options);
							}

							return self;
						}
					},

					undo : function(event) {
						var self = this;
						var last = self.undoStack.pop();

						if (last) {
							self.redoStack.push(last);

							switch (last[0]) {
							case 'left':
								self.moveToRight(last[1], event, false, true);
								break;
							case 'right':
								self.moveToLeft(last[1], event, false, true);
								break;
							}
						}
					},
					redo : function(event) {
						var self = this;
						var last = self.redoStack.pop();

						if (last) {
							self.undoStack.push(last);

							switch (last[0]) {
							case 'left':
								self.moveToLeft(last[1], event, false, true);
								break;
							case 'right':
								self.moveToRight(last[1], event, false, true);
								break;
							}
						}
					}
				}

				return Multiselect;
			})($);

			$.multiselect = {
				defaults : {
					/**
					 * will be executed once - remove from $left all options
					 * that are already in $right
					 * 
					 * @method startUp
					 * @attribute $left jQuery object
					 * @attribute $right jQuery object
					 */
					startUp : function($left, $right) {
						$right.find('option').each(
								function(index, option) {
									var $option = $left.find('option[value="'
											+ option.value + '"]');
									var $parent = $option.parent();

									$option.remove();

									if ($parent.prop('tagName') == 'OPTGROUP') {
										$parent.removeIfEmpty();
									}
								});
					},

					/**
					 * will be executed each time before moving option[s] to
					 * right
					 * 
					 * IMPORTANT : this method must return boolean value true :
					 * continue to moveToRight method false : stop
					 * 
					 * @method beforeMoveToRight
					 * @attribute $left jQuery object
					 * @attribute $right jQuery object
					 * @attribute $options HTML object (the option[s] which was
					 *            selected to be moved)
					 * 
					 * @default true
					 * @return {boolean}
					 */
					beforeMoveToRight : function($left, $right, $options) {
						return true;
					},

					/*
					 * will be executed each time after moving option[s] to
					 * right
					 * 
					 * @method afterMoveToRight @attribute $left jQuery object
					 * @attribute $right jQuery object @attribute $options HTML
					 * object (the option[s] which was selected to be moved)
					 */
					afterMoveToRight : function($left, $right, $options) {
					},

					/**
					 * will be executed each time before moving option[s] to
					 * left
					 * 
					 * IMPORTANT : this method must return boolean value true :
					 * continue to moveToRight method false : stop
					 * 
					 * @method beforeMoveToLeft
					 * @attribute $left jQuery object
					 * @attribute $right jQuery object
					 * @attribute $options HTML object (the option[s] which was
					 *            selected to be moved)
					 * 
					 * @default true
					 * @return {boolean}
					 */
					beforeMoveToLeft : function($left, $right, $options) {
						return true;
					},

					/*
					 * will be executed each time after moving option[s] to left
					 * 
					 * @method afterMoveToLeft @attribute $left jQuery object
					 * @attribute $right jQuery object @attribute $options HTML
					 * object (the option[s] which was selected to be moved)
					 */
					afterMoveToLeft : function($left, $right, $options) {
					},

					/**
					 * sort options by option text
					 * 
					 * @method sort
					 * @attribute a HTML option
					 * @attribute b HTML option
					 * 
					 * @return 1/-1
					 */
					sort : function(a, b) {
						if (a.innerHTML == 'NA') {
							return 1;
						} else if (b.innerHTML == 'NA') {
							return -1;
						}

						return (a.innerHTML > b.innerHTML) ? 1 : -1;
					}
				}
			};

			var ua = window.navigator.userAgent;
			var isIE = (ua.indexOf("MSIE ") + ua.indexOf("Trident/") + ua
					.indexOf("Edge/")) > -3;

			$.fn.multiselect = function(options) {
				return this.each(function() {
					var $this = $(this), data = $this.data();

					var settings = $.extend({}, $.multiselect.defaults, data,
							options);

					return new Multiselect($this, settings);
				});
			};

			// append options
			// then set the selected attribute to false
			$.fn.move = function($options) {
				this.append($options).find('option').prop('selected', false);

				return this;
			};

			$.fn.removeIfEmpty = function() {
				if (!this.children().length) {
					this.remove();
				}

				return this;
			};

			$.fn.mShow = function() {
				this.removeClass('hidden').show();

				if (isIE) {
					this.each(function(index, option) {
						// Remove <span> to make it compatible with IE
						if ($(option).parent().is('span')) {
							$(option).parent().replaceWith(option);
						}

						$(option).show();
					});
				}

				return this;
			};

			$.fn.mHide = function() {
				this.addClass('hidden').hide();

				if (isIE) {
					this.each(function(index, option) {
						// Wrap with <span> to make it compatible with IE
						if (!$(option).parent().is('span')) {
							$(option).wrap('<span>').hide();
						}
					});
				}

				return this;
			};

			// sort options then reappend them to the select
			$.fn.mSort = function(callback) {
				this.find('option').sort(callback).appendTo(this);

				return this;
			};

			$.expr[":"].search = $.expr.createPseudo(function(arg) {
				return function(elem) {
					return $(elem).text().toUpperCase().indexOf(
							arg.toUpperCase()) >= 0;
				};
			});
		}));